package com.nifostasky.comic.common.tokenfilter;


import com.alibaba.fastjson.JSONObject;
import com.nifostasky.comic.common.threadlocal.LocalData;
import com.nifostasky.comic.config.TokenConfig;
import com.nifostasky.comic.exceptions.BusinessException;
import com.nifostasky.comic.utils.JwtUtil;
import com.nifostasky.comic.utils.RedisKeysConstantUtil;
import com.nifostasky.comic.vos.ResultCode;
import io.jsonwebtoken.Claims;
import lombok.SneakyThrows;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 *
 *  过滤器:  获取验证用户信息
 * @author : Administrator
 * @date : 2021/8/18 15:32
 *
 */
@Component
public class TokenFilter implements Filter {
    @Autowired
    private TokenConfig tokenConfig;

    @Resource
    private RedisTemplate redisTemplate;

    //放行URL 具体路径
    @SneakyThrows
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain){
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        //跨域配置
        httpResponse.addHeader("Access-Control-Allow-Credentials", "true");
        httpResponse.addHeader("Access-Control-Allow-Origin", "*");
        httpResponse.addHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT");
        httpResponse.addHeader("Access-Control-Allow-Headers", "Content-Type,token");

        LocalData localData = new LocalData();
        Boolean flag = this.handleWhiteList(httpRequest, response, chain);
        if (flag){
            //如果不需要拦截则无需执行后面内容
            return;
        }

        String token = null;
        token = httpRequest.getHeader("token");


        //token为空,抛出自定义异常 701; 重新登录
        if (StringUtils.isBlank(token)) {
            throw new BusinessException(ResultCode.CLOUD_USER_INVALID_TOKEN);
        }

        Claims claims =  null;
        try{
            claims = new JwtUtil().parseJWT(token);
            //token过期,抛出自定义异常 701; 重新登录
            if(new Date().after(claims.getExpiration())){
                throw new BusinessException(ResultCode.CLOUD_USER_INVALID_TOKEN);
            }
        }catch (Exception e){
            //token格式等其他错误,抛出自定义异常 701; 重新登录
            throw new BusinessException(ResultCode.CLOUD_USER_INVALID_TOKEN);
        }



        JSONObject jsonObject = JSONObject.parseObject((String) claims.get("sub"));
        String issuer = claims.getIssuer();
        if (jsonObject.containsKey("userId")){
            Long userId = jsonObject.getLong("userId");
            localData.setUserId(userId);
        }
        if (jsonObject.containsKey("userName")){
            String userName = jsonObject.getString("userName");
            localData.setUserName(userName);
        }
        if(jsonObject.containsKey("superAdmin")){
            localData.setSuperAdmin(jsonObject.getBoolean("superAdmin"));
        }
        if(jsonObject.containsKey("email")){
            localData.setEmail(jsonObject.getString("email"));
        }
        //设置用户信息
        LocalDataControl.setLocalData(localData);

        //获取redis中的token;更新过期时间
        Object redisToken = redisTemplate.opsForValue().get(RedisKeysConstantUtil.TOKEN_PREFIX + localData.getUserId());
        //token在redis中过期,抛出自定义异常 701; 重新登录
        if (Objects.isNull(redisToken)) {
            throw new BusinessException(ResultCode.CLOUD_USER_INVALID_TOKEN);
        }
        //更新过期时间
        redisTemplate.expire(RedisKeysConstantUtil.TOKEN_PREFIX + localData.getUserId(),tokenConfig.getTokenRedisExpireTime(), TimeUnit.HOURS);

        // 继续执行
        chain.doFilter(request, response);
    }


    //放行url路径
    private Boolean handleWhiteList(HttpServletRequest request, ServletResponse response,FilterChain chain) throws IOException, ServletException {
        Boolean flag = false; //判断是否需要拦截; false表示需要拦截 ; true表示不需要拦截
        String path = request.getServletPath();
        if (path.contains("/doc.html") || path.contains("/swagger-ui.html") || path.contains("/webjars")
                || path.contains("/swagger-resources") || path.contains("/configuration")
                || path.contains("/v2/api-docs") || path.contains("/v2/api-docs-ext")
                || path.contains("/login") || path.contains("/user")
                || path.contains("/agentController")
        ) {
            flag = true; //表示 不需要拦截
            chain.doFilter(request, response);
        }
        return flag;
    }
}
